xlv.obj.dm={//dataModel
	extend:function(){
	},
	implement:function(){
		this.implement('xlv.imp.xml');
	},
	create:function(tag){
		var div=document.createElement((tag||'div'));
		div.setAttribute('xlvobject','xlv.obj.dm');
		return div;			
	},
	properties:{
		_host:'',
		_servlet:'/xlive/web',
		globe:true,
		name:null,
		notifyRequest:true,
		objectURL:null,
		methodName:null,
		argumentsXML:null,
		initialJSONData:null
	},
	method:{
		initial:function(){
			this.$V.updated=false;
			this.$V.mvcArray=[];
			this.$V.lastMethodName='';
			this.$V.__started=false;
			this.$V.__refCount=0;
			this.$V.__mvcArray=null;
			this.$V.__responsed=false;
			this.$V.__disposed=false;
		},
		start:function(){
			if(this.$V.__started){
				if(this.$P.notifyRequest && this.$V.__mvcArray){
					var mvc_array=this.$V.__mvcArray;
					this.$V.__mvcArray=null;
					var memo=this.createMemoObject({type:'create',methods:'start',from:this.$P.name,to:this.$P.objectURL,cancelable:false});
					this.$V.__queueNotifyMVC=true;
					this.notifyMVC('beforeModelChanged','setChanged',memo,mvc_array);
					this.notifyMVC('modelChanged','setChanged',memo,mvc_array);
					this.notifyMVC('afterModelChanged','setChanged',memo,mvc_array);
					this.$V.__queueNotifyMVC=false;
				}
			}else{
				if(this.$P.initialJSONData!=null)
					this.queueTimeout((function(){this.loadFromJSON(this.$P.initialJSONData);}).bind(this));
			}
			this.$V.__started=true;
		},
		isStarted:function(){
			return this.$V.__started;
		},
		responsed:function(){
			return (this.$V.__started&&this.$V.__responsed);
		},
		isDisposed:function(){
			return this.$V.__disposed;
		},
		dispose:function(){
			if(this.$P.globe&&this.$V.__refCount>0) return;
			this.clearListener();
			this.queueTimeout(
					function(){
						this.xml_properties.currentXML=null;
						this.xml_properties.responseXML=null;
						this.$V.__disposed=true;
					}.bind(this));
		},
		loadFromJSON:function(json){
			this.loadFromString(json2xml(json));
		},
		loadFromString:function(xmlstring){
			if(xmlstring!=null){
				if(this.objectName()=='xlv.obj.dm')xmlstring='<root>'+xmlstring+'</root>';//??????? add root
				this.xml_loadFromString(xmlstring);
				if(this.objectName()=='xlv.obj.dm'){
					this.notifyMVC('modelChanged','nodeChanged',this.createMemoObject({type:'update',src:this.xml_properties.currentXML.documentElement,methods:'loadFromString',from:xmlstring,to:this}));
				}
				this.$V.__responsed=true;
				this.response();
			}
		},
		extendFromJSON:function(json){
			var current=this.xml_properties.currentXML;
			if(current==null)return;
			var xmlstring=json2xml(json);
			if(this.objectName()=='xlv.obj.dm')xmlstring='<root>'+xmlstring+'</root>';//??????? add root
			var xml=this.xml_parseFromString(xmlstring);
			if(current.documentElement)xlv.util.xmlExtendNodes(current.documentElement,xml,'overwrite',false);
		},
		toJSObject:function(){
			var json=xml2json(this.xml_properties.currentXML.documentElement,'');
			var jso=json.evalJSON();
			return (jso&&jso.root)?jso.root:jso;
		},
		createMemoObject:function(memo){
			momo=xlv.sys.createMemoObject(memo);
			if(memo.src===null)memo.src=this;
			return memo;
		},
		getName:function(){
			return (this.$P.name||this.$P.objectURL);
		},
		_makeObjectURL:function(object_url){
			var url=(object_url||this.$P.objectURL);
			if(!url)return null;
			url=url.replace(/\./g, '/');
			return this.$P._host+this.$P._servlet+(url.charAt(0)=='/' ? '' : '/')+url;
		},
		_makeMethodArguments:function(method_name,arg_xml){
			this.$V.__responsed=false;
			var arg=(arg_xml||this.$P.argumentsXML||this.xml_createElement('arguments'));
			if(typeof arg != 'string') arg=this.xml_nodeToString(arg);
			if(arg!='<arguments/>'){
				if(!arg.startsWith('<arguments>')) arg='<arguments>'+arg;
				if(!arg.endsWith('</arguments>')) arg=arg+'</arguments>';
			}
			return '<method name="'+(method_name||this.$P.methodName)+'">'+arg+'</method>';
		},
		request:function(object,method_name,arg_xml){
			this.$V.__responsed=false;
			var url=this._makeObjectURL(object);
			if(!url){
				alert('dm.request : url is null');
				return;
			}
			this.$V.lastMethodName=(method_name||this.$P.methodName);
			if(this.$P.notifyRequest){
				var memo=this.createMemoObject({type:this.$V.lastMethodName,methods:this.$V.lastMethodName,from:this.$P.name,to:this.$P.objectURL});
				this.notifyMVC('beforeModelChanged','request',memo);
				if(memo.cancel)return false;
			}
			var options={}; 
			options.postBody='<xlive>'+this._makeMethodArguments(method_name,arg_xml)+'</xlive>';
			this.xml_request(url,options);
			return true;
		},
		requestMethods:function(object,methods){
			var url=this._makeObjectURL(object);
			if(!url){
				alert('dm.requestMethods : url is null');
				return;
			}
			if(this.$P.notifyRequest){
				var memo=this.createMemoObject({type:'requestMethods',from:this.$P.name,to:this.$P.objectURL});
				this.notifyMVC('beforeModelChanged','request',memo);
				if(memo.cancel)return false;
			}
			var options={};
			options.postBody='<xlive>'+methods+'</xlive>';
			this.xml_request(url,options);
			return true;
		},
		getReadyState:function(){
			return this.xml_readyState();
		},
		nodeIndexes:function(node){
			return this.xml_nodeIndexes(node,((arguments.length>1&&Object.isElement(arguments[1]))?arguments[1]:null));
		},
		getNode:function(xpath){
			if(!xpath||xpath.length==0) return this.xml_properties.currentXML.documentElement;
			xpath=xpath.replace(/\./g,'/');
			return this.xml_selectSingleNode(xpath,((arguments.length>1&&Object.isElement(arguments[1]))?arguments[1]:null));
		},
		getValue:function(xpath){
			var node=this.getNode(xpath,((arguments.length>1&&Object.isElement(arguments[1]))?arguments[1]:null));
			return node ? this.xml_getNodeValue(node) : '';
		},
		setValue:function(value,xpath){
			var node=this.getNode(xpath,((arguments.length>2&&Object.isElement(arguments[2]))?arguments[2]:null));
			if(node) {
				var old=this.xml_getNodeValue(node);
				if(old!=value){
					this.xml_setNodeValue(node,value);
					this.$V.updated=true;
					this.notifyMVC('modelChanged','nodeChanged',this.createMemoObject({type:'update',methods:'setValue',src:node,from:old,to:value}));
				}
			}
		},
		getText:function(xpath){
			var node=this.getNode(xpath,((arguments.length>1&&Object.isElement(arguments[1]))?arguments[1]:null));
			return node?this.xml_getNodeText(node) : '';
		},
		setText:function(text,xpath){
			var node=this.getNode(xpath,((arguments.length>2&&Object.isElement(arguments[2]))?arguments[2]:null));
			if(node) {
				var old=this.xml_getNodeText(node);
				if(old!=text){
					this.xml_setNodeText(node,text);
					this.$V.updated=true;
					this.notifyMVC('modelChanged','nodeChanged',this.createMemoObject({type:'update',methods:'setText',src:node,from:old,to:text}));
				}
			}
		},
		remove:function(xpath){
			var node=(Object.isElement(xpath))?xpath:this.getNode(xpath,((arguments.length>1&&Object.isElement(arguments[1]))?arguments[1]:null));
			if(node){
				this.$V.updated=true;
				node.parentNode.removeChild(node);
				this.notifyMVC('modelChanged','nodeChanged',this.createMemoObject({type:'remove',methods:'remove',src:node,from:node,to:node}));
			}
			return node;
		},
		insert:function(tag_names,value){
			var root_node=((arguments.length>2&&Object.isElement(arguments[2]))?arguments[2]:null);
			root_node=(root_node||this.xml_properties.currentXML.documentElement);
			var elements=xml_createElements(tag_names,value);
			var source=this.xml_getDocument().createElement('source');
			source.appendChild(elements[0]);
			xlv.util.xmlExtendNodes(root_node,source,'append',false);
			this.$V.updated=true;
			var last=elements[elements.length-1];
			this.notifyMVC('modelChanged','nodeChanged',this.createMemoObject({type:'insert',methods:'insert',src:last,from:elements[0],to:last}));
			return last;
		},
		replace:function(new_node,xpath){
			var old_node=(Object.isElement(xpath))?xpath:this.getNode(xpath,((arguments.length>2&&Object.isElement(arguments[2]))?arguments[2]:null));
			if(old_node){
				this.$V.updated=true;
				old_node.parentNode.replaceChild(new_node,old_node);
				this.notifyMVC('modelChanged','nodeChanged',this.createMemoObject({type:'update',methods:'replace',src:old_node,from:old_node,to:new_node}));
			}
		},
		substitute:function(new_node,xpath){
			var old_node=(Object.isElement(xpath))?xpath:this.getNode(xpath,((arguments.length>2&&Object.isElement(arguments[2]))?arguments[2]:null));
			if(old_node){
				this.$V.updated=true;
				while(old_node.firstChild)new_node.appendChild(old_node.firstChild);
				old_node.parentNode.replaceChild(new_node,old_node);
				this.notifyMVC('modelChanged','nodeChanged',this.createMemoObject({type:'update',methods:'substitute',src:old_node,from:old_node,to:new_node}));
			}
		},
		/***/
		notifyMVC:function(mvc_method,notify,memo,mvc_array){
			if(this.$V.__queueNotifyMVC ||mvc_method=='modelChanged'||mvc_method=='afterModelChanged')
				this.queueTimeout(function(){this._notifyMVC(mvc_method,notify,memo,mvc_array);}.bind(this));
			else this._notifyMVC(mvc_method,notify,memo,mvc_array);
			//if(memo.cancelable===true)this._notifyMVC(mvc_method,notify,memo,mvc_array);
			//else this.queueTimeout(function(){this._notifyMVC(mvc_method,notify,memo,mvc_array);}.bind(this));
		},
		_notifyMVC:function(mvc_method,notify,memo,mvc_array){
			memo=(memo||this.createMemoObject());
			mvc_array=(mvc_array||this.$V.mvcArray);
			for(var i=0,n=mvc_array.length;i<n;++i){
				if(mvc_array[i]&&mvc_array[i]['mvc_'+mvc_method]) {
					mvc_array[i]['mvc_'+mvc_method](this,notify,memo);
					//mvc_array[i]['notified']({when:mvc_method,'notify':notify,type:memo.type});
					if(memo.cancelable===true && memo.cancel===true)break;
				}
			}				
		},
		addListener:function(element){
			if(this.responsed()){
				if(!this.$V.__mvcArray)this.$V.__mvcArray=[];
				this.$V.__mvcArray.push(element);
				this.getDesktop().dmc_dmRegisterStart(this);
			}
			this.$V.mvcArray.push(element);
		},
		removeListener:function(element){
			for(var i=0,n=this.$V.mvcArray.length;i<n;++i){
				if(this.$V.mvcArray[i]==element) return this.$V.mvcArray.splice(i,1);
			}
			return null;
		},
		clearListener:function(){
			this.$V.mvcArray.length = 0;
		},
		broadcastEnableControl:function(){
			this.queueTimeout(function(){this._notifyMVC('mvc_enableControl');}.bind(this));
		},
		/**be overrided**/
		response:function(){
			this._notifiyReady();
			var memo=this.createMemoObject({type:'create',methods:this.xml_responseMethodNames(),from:this.$P.name,to:this.$P.objectURL,cancelable:false});
			this.notifyMVC('beforeModelChanged','setChanged',memo);
			this.notifyMVC('modelChanged','setChanged',memo);
			this.notifyMVC('afterModelChanged','setChanged',memo);
		},
		_notifiyReady:function(){
			var memo=xlv.sys.createMemoObject();
			memo.type='dm_ready';
			memo.methods=this.xml_responseMethodNames();
			memo.src=this;
			memo.from=this;
			memo.to=this;
			setTimeout((function(){Event.fire(this,'xlv:dm_ready',memo);}).bind(this),1);
		},
		/**override xml**/
		xml_response:function(){
			this.$V.updated=false;
			if(this.$P.notifyRequest){
				this.notifyMVC('afterModelChanged','request',this.createMemoObject({type:'create',methods:this.xml_responseMethodNames(),from:this.$P.name,to:this.$P.objectURL}));
			}
			this.$V.__responsed=true;
			this.response();
		}
	}
};

